/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "controller/lottie_controller_bridge.h"
#include "common/napi_helper.h"
#include "common/number_constant.h"
#include "node/lottie_listener.h"
#include "node/lottie_manager.h"
#include "task_pool/task_pool.h"
#include <cstdint>
#include <map>

namespace LottieTurbo {

static const std::unordered_map<std::string, LottieEventType> listenerEventMap = {
    {"drawFrame", LottieEventType::ON_DRAW_FRAME},       {"enterFrame", LottieEventType::ON_ENTER_FRAME},
    {"loopComplete", LottieEventType::ON_LOOP_COMPLETE}, {"complete", LottieEventType::ON_COMPLETE},
    {"segmentStart", LottieEventType::ON_SEGMENT_START}, {"destroy", LottieEventType::ON_DESTROY},
    {"config_ready", LottieEventType::ON_CONFIG_READY},  {"data_ready", LottieEventType::ON_DATA_READY},
    {"DOMLoaded", LottieEventType::ON_DOM_LOADED},       {"error", LottieEventType::ON_ERROR},
    {"data_failed", LottieEventType::ON_DATA_FAILED},    {"loaded_images", LottieEventType::ON_LOADED_IMAGES}};

static const size_t EVENT_LISTENER_ARGC = 2;
static const size_t PLAY_SEGMENT_ARGC = 2;
static const size_t SEGMENT_SIZE = 2;
static const size_t GO_TO_ARGS_ONE_OPTIONAL = 2;
static const size_t GO_TO_ARGS_TWO_OPTIONAL = 3;

napi_value LottieControllerBridge::HandleLottieBaseAction(napi_env env, napi_callback_info info,
                                                          LottieBaseAction action)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);

    if (obj == nullptr) {
        DRAWING_LOGE("LottieControllerBridge::HandleLottieBaseAction: Failed to unwrap "
                     "instance from callback in LottieControllerBridge::HandleLottieAction()");
        return nullptr;
    }

    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler == nullptr) {
        DRAWING_LOGE("LottieControllerBridge::HandleLottieBaseAction lottieId(%{public}s): "
                     "Failed to get LottieHandler",
                     obj->lottieId_.c_str());
        return nullptr;
    }

    switch (action) {
        case LottieBaseAction::PLAY :
            DRAWING_LOGD("LottieControllerBridge::Play() called, id: %{public}s", obj->lottieId_.c_str());
            lottieHandler->Play();
            break;
        case LottieBaseAction::PAUSE :
            DRAWING_LOGD("LottieControllerBridge::Pause() called, id: %{public}s", obj->lottieId_.c_str());
            lottieHandler->Pause();
            break;
        case LottieBaseAction::STOP :
            DRAWING_LOGD("LottieControllerBridge::Stop() called, id: %{public}s", obj->lottieId_.c_str());
            lottieHandler->Stop();
            break;
        case LottieBaseAction::TOGGLE_PAUSE :
            DRAWING_LOGD("LottieControllerBridge::TogglePause() called, id: %{public}s", obj->lottieId_.c_str());
            lottieHandler->TogglePause();
            break;
        default :
            DRAWING_LOGE("LottieControllerBridge::HandleLottieBaseAction lottieId(%{public}s): "
                         "Invalid action requested in LottieControllerBridge::HandleLottieAction()",
                         obj->lottieId_.c_str());
            return nullptr;
    }

    return nullptr;
}

napi_value LottieControllerBridge::Play(napi_env env, napi_callback_info info)
{
    return LottieControllerBridge::HandleLottieBaseAction(env, info, LottieBaseAction::PLAY);
}

napi_value LottieControllerBridge::Pause(napi_env env, napi_callback_info info)
{
    return LottieControllerBridge::HandleLottieBaseAction(env, info, LottieBaseAction::PAUSE);
}

napi_value LottieControllerBridge::Stop(napi_env env, napi_callback_info info)
{
    return LottieControllerBridge::HandleLottieBaseAction(env, info, LottieBaseAction::STOP);
}

napi_value LottieControllerBridge::TogglePause(napi_env env, napi_callback_info info)
{
    return LottieControllerBridge::HandleLottieBaseAction(env, info, LottieBaseAction::TOGGLE_PAUSE);
}

napi_value LottieControllerBridge::Destroy(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto handler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (handler == nullptr || handler->isDestroy_) {
        return nullptr;
    }
    handler->isDestroy_ = true;
    TaskPoolLocal::GetInstance().PushTask(
        [](std::string id) {
            auto handler = LottieManager::GetInstance().GetHandleById(id);
            if (handler == nullptr) {
                return;
            }
            auto func = [](std::string id) { LottieManager::GetInstance().DisposeLottieNode(id); };
            auto trigger = new LottieHandler::TriggerData;
            trigger->func = std::bind(func, id);
            napi_call_threadsafe_function(handler->safeFunc_, trigger, napi_tsfn_blocking);
        },
        obj->lottieId_);
    return nullptr;
}

napi_value LottieControllerBridge::GoToAndStop(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    if (args.size() == 0) {
        return nullptr;
    }
    auto value = napiHelper.GetDouble(args[NUM_0]);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    DRAWING_LOGD("LottieControllerBridge::GoToAndStop() called, id: %{public}s", obj->lottieId_.c_str());
    if (lottieHandler == nullptr) {
        return nullptr;
    }
    std::optional<bool> isFrame = std::nullopt;
    if (args.size() == GO_TO_ARGS_ONE_OPTIONAL) {
        isFrame = napiHelper.GetBoolean(args[NUM_1]);
    }
    lottieHandler->GoToAndStop(value, isFrame);

    return nullptr;
}

napi_value LottieControllerBridge::GoToAndPlay(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    if (args.size() == 0) {
        return nullptr;
    }
    auto value = napiHelper.GetDouble(args[NUM_0]);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    DRAWING_LOGD("LottieControllerBridge::GoToAndPlay() called, id: %{public}s", obj->lottieId_.c_str());
    if (lottieHandler == nullptr) {
        return nullptr;
    }
    std::optional<bool> isFrame = std::nullopt;
    if (args.size() == GO_TO_ARGS_ONE_OPTIONAL) {
        isFrame = napiHelper.GetBoolean(args[NUM_1]);
    }
    lottieHandler->GoToAndPlay(value, isFrame);

    return nullptr;
}

napi_value LottieControllerBridge::SetSegment(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    if (args.size() != SEGMENT_SIZE) {
        return nullptr;
    }
    auto initFrame = napiHelper.GetDouble(args[NUM_0]);
    auto endFrame = napiHelper.GetDouble(args[NUM_1]);

    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    DRAWING_LOGD("LottieControllerBridge::SetSegment() called, id: %{public}s", obj->lottieId_.c_str());
    if (lottieHandler != nullptr) {
        lottieHandler->SetSegments({static_cast<float>(initFrame), static_cast<float>(endFrame)});
    }
    return nullptr;
}

napi_value LottieControllerBridge::PlaySegments(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    if (args.empty()) {
        return nullptr;
    }
    auto isArray = napiHelper.CheckIsArray(args[NUM_0]);
    if (!isArray) {
        return nullptr;
    }
    auto arrayLength = napiHelper.GetArrayLength(args[NUM_0]);
    std::vector<AnimationSegment> segments;
    for (int i = 0; i < arrayLength; i++) {
        auto element = napiHelper.GetArrayElement(args[NUM_0], i);
        auto elementLength = napiHelper.GetArrayLength(element);
        if (elementLength == SEGMENT_SIZE) {
            auto init = napiHelper.GetDouble(napiHelper.GetArrayElement(element, NUM_0));
            auto end = napiHelper.GetDouble(napiHelper.GetArrayElement(element, NUM_1));
            segments.push_back({.initFrame = static_cast<float>(init), .endFrame = static_cast<float>(end)});
        }
    }

    if (!segments.empty()) {
        auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
        if (obj == nullptr) {
            return nullptr;
        }
        auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
        DRAWING_LOGD("LottieControllerBridge::SetSegment() called, id: %{public}s", obj->lottieId_.c_str());
        if (lottieHandler != nullptr) {
            if (args.size() == PLAY_SEGMENT_ARGC) {
                bool forceFlag = napiHelper.GetBoolean(args[NUM_1]);
                lottieHandler->SetForceFlag(forceFlag);
            }
            lottieHandler->SetSegments(std::move(segments));
            lottieHandler->Play();
        }
    }

    return nullptr;
}

napi_value LottieControllerBridge::ResetSegments(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    if (args.empty()) {
        return nullptr;
    }
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    DRAWING_LOGD("LottieControllerBridge::ResetSegments() called, id: %{public}s", obj->lottieId_.c_str());
    if (lottieHandler != nullptr) {
        bool forceFlag = napiHelper.GetBoolean(args[NUM_0]);
        lottieHandler->ResetSegments(forceFlag);
    }
    return nullptr;
}

napi_value LottieControllerBridge::SetSpeed(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    if (args.empty()) {
        return nullptr;
    }
    double speed = napiHelper.GetDouble(args[NUM_0]);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }

    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    DRAWING_LOGD("LottieControllerBridge::SetSpeed() called, id: %{public}s", obj->lottieId_.c_str());
    if (lottieHandler != nullptr) {
        lottieHandler->SetSpeed(speed);
    }
    return nullptr;
}

napi_value LottieControllerBridge::SetDirection(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    if (args.empty()) {
        return nullptr;
    }
    int direction = napiHelper.GetInteger(args[NUM_0]);
    DRAWING_LOGD("LottieControllerBridge::SetDirection() direction: %{public}d", direction);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }

    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    DRAWING_LOGD("LottieControllerBridge::SetDirection() called, id: %{public}s", obj->lottieId_.c_str());
    if (lottieHandler != nullptr) {
        lottieHandler->SetDirection(direction);
    }
    return nullptr;
}

napi_value LottieControllerBridge::SetSubframe(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    auto isSubframeEnabled = napiHelper.GetBoolean(args[NUM_0]);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        lottieHandler->SetIsSubframeEnabled(isSubframeEnabled);
        DRAWING_LOGD("SetSubframe %{public}d", isSubframeEnabled);
    }
    return nullptr;
}

napi_value LottieControllerBridge::TriggerEvent(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    auto eventName = napiHelper.GetString(args[NUM_0]);
    auto it = listenerEventMap.find(eventName);
    if (it == listenerEventMap.end()) {
        return nullptr;
    }
    if (lottieHandler != nullptr && lottieHandler->lottieListener_ != nullptr) {
        lottieHandler->lottieListener_->TriggerEvent(it->second);
    }
    return nullptr;
}

napi_value LottieControllerBridge::AddEventListener(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    if (args.size() < EVENT_LISTENER_ARGC) {
        return nullptr;
    }
    auto eventName = napiHelper.GetString(args[NUM_0]);

    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    DRAWING_LOGD("LottieControllerBridge::AddEventListener() called, id: %{public}s", obj->lottieId_.c_str());
    if (lottieHandler != nullptr) {
        auto it = listenerEventMap.find(eventName);
        if (it == listenerEventMap.end()) {
            return nullptr;
        }
        lottieHandler->lottieListener_->RegisterCallback(args[NUM_1], it->second);
    }
    return nullptr;
}

napi_value LottieControllerBridge::RemoveEventListener(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    auto eventName = napiHelper.GetString(args[NUM_0]);

    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    DRAWING_LOGD("LottieControllerBridge::RemoveEventListener() called, id: %{public}s", obj->lottieId_.c_str());
    if (lottieHandler != nullptr) {
        auto it = listenerEventMap.find(eventName);
        if (it == listenerEventMap.end()) {
            return nullptr;
        }
        lottieHandler->lottieListener_->UnRegisterCallback(it->second);
        // 如果没有回调，直接返回
        if (args.size() < EVENT_LISTENER_ARGC) {
            return nullptr;
        }
        auto global = napiHelper.GetGlobal();
        napiHelper.Call(global, args[NUM_1], 0, nullptr);
    }
    return nullptr;
}

napi_value LottieControllerBridge::ChangeColor(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    if (args.empty()) {
        return nullptr;
    }
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler == nullptr) {
        return nullptr;
    }
    auto layerName = napiHelper.GetString(args[NUM_0]);
    if (napiHelper.CheckIsType(args[NUM_1], napi_null)) {
        return nullptr;
    }
    if (napiHelper.CheckIsType(args[NUM_1], napi_undefined)) {
        return nullptr;
    }
    if (args.size() == NUM_2) {
        auto length = napiHelper.GetArrayLength(args[NUM_1]);
        std::vector<float> color;
        for (int i = 0; i < length; i++) {
            auto num = napiHelper.GetDouble(napiHelper.GetArrayElement(args[NUM_1], i));
            color.push_back(num);
        }
        lottieHandler->ChangeColor(layerName, color);
    } else if (args.size() == NUM_3) {
        if (napiHelper.CheckIsType(args[NUM_2], napi_null)) {
            return nullptr;
        }
        if (napiHelper.CheckIsType(args[NUM_2], napi_undefined)) {
            return nullptr;
        }
        auto startLenth = napiHelper.GetArrayLength(args[NUM_1]);
        auto endLenth = napiHelper.GetArrayLength(args[NUM_2]);
        std::vector<float> startColor;
        std::vector<float> endColor;
        for (int i = NUM_0; i < startLenth; i++) {
            auto num = napiHelper.GetDouble(napiHelper.GetArrayElement(args[NUM_1], i));
            startColor.push_back(num);
        }
        for (int i = NUM_0; i < endLenth; i++) {
            auto num = napiHelper.GetDouble(napiHelper.GetArrayElement(args[NUM_2], i));
            endColor.push_back(num);
        }
        lottieHandler->ChangeColor(layerName, startColor, endColor);
    }
    DRAWING_LOGD("LottieControllerBridge::ChangeColor LayerName:%{public}s", layerName.c_str());
    return nullptr;
}

void LottieControllerBridge::UpdateLottieConfig(napi_value config,
                                                std::shared_ptr<LottieConfigBridge>& configData,
                                                std::shared_ptr<LottieHandler>& lottieHandler)
{
    if (configData->GetResManager()) {
        lottieHandler->dataManager_->mNativeResMgr_ = configData->GetResManager();
    }
    std::variant<bool, int> loop{false};
    if (configData->GetConfigLoopValue(config, loop, "loop")) {
        lottieHandler->GetLottieConfig()->SetLoop(loop);
    }

    bool autoplay = false;
    if (configData->GetConfigValue(config, autoplay, "autoplay")) {
        lottieHandler->GetLottieConfig()->SetAutoplay(autoplay);
    }

    std::string path;
    Resource resource;
    if (configData->GetConfigValue(config, path, "path")) {
        lottieHandler->GetLottieConfig()->SetPath(path);
    } else if (configData->GetResource(config, resource, "path")) {
        lottieHandler->GetLottieConfig()->SetResource(resource);
    }

    AnimationSegment initialSegment;
    if (configData->GetConfigValue(config, initialSegment, "initialSegment")) {
        lottieHandler->GetLottieConfig()->SetInitialSegment(initialSegment);
    }

    std::string contentMode;
    if (configData->GetConfigValue(config, contentMode, "contentMode")) {
        lottieHandler->GetLottieConfig()->SetContentMode(contentMode);
    }

    double frameRate;
    constexpr int maxFrameRate = 120;
    if (configData->GetConfigValue(config, frameRate, "frameRate")) {
        if (frameRate > 0 && frameRate <= maxFrameRate) {
            lottieHandler->SetFrameRate(frameRate);
        }
    } else {
        lottieHandler->SetFrameRate(0);
    }

    std::string animationData;
    if (configData->GetConfigValue(config, animationData, "animationData")) {
        lottieHandler->GetLottieConfig()->SetAnimationData(animationData);
    }

    auto &imgAssets = const_cast<ImageAssetDelegateType &>(lottieHandler->
                      GetLottieConfig()->GetImageAssetDelegate());
    imgAssets->clear();
    if (configData->GetConfigValue(config, imgAssets, "setImageAssetDelegate") &&
        lottieHandler->dataManager_ != nullptr) {
        lottieHandler->dataManager_->SetImageAssetDelegate(imgAssets);
    }
}

napi_value LottieControllerBridge::ReLoad(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    if (args.empty()) {
        return nullptr;
    }

    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    auto configData = std::make_shared<LottieConfigBridge>(env, args[NUM_0]);
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler == nullptr) {
        return nullptr;
    }

    // 更新配置
    UpdateLottieConfig(args[NUM_0], configData, lottieHandler);
    DRAWING_LOGD("LottieControllerBridge::ReLoad");
    // 执行重载任务
    TaskPoolLocal::GetInstance().PushTask(
        [](std::shared_ptr<LottieHandler> handler) {
            if (handler->isDestroy_) {
                return;
            }
            auto ret = handler->Reload();
            if (!ret) {
                return;
            }
            if (handler->updateContentMode) {
                handler->updateContentMode();
            }
            if (handler->GetLottieConfig()->GetAutoplay()) {
                handler->Play();
            } else if (handler->renderAndAdvanceFrame) {
                handler->renderAndAdvanceFrame();
            }
        },
        lottieHandler);

    return nullptr;
}

napi_value LottieControllerBridge::SetContentMode(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    auto contentMode = napiHelper.GetString(args[NUM_0]);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler == nullptr) {
        return nullptr;
    }
    lottieHandler->GetLottieConfig()->SetContentMode(contentMode);
    DRAWING_LOGD("LottieControllerBridge::SetContentMode ContentMode:%{public}s", contentMode.c_str());
    if (lottieHandler->updateContentMode) {
        lottieHandler->updateContentMode();
    }
    if (lottieHandler->renderCurrentFrame) {
        lottieHandler->renderCurrentFrame();
    }
    return nullptr;
}

napi_value LottieControllerBridge::SetFrameRate(napi_env env, napi_callback_info info)
{
    constexpr int frameRateLimits = 120;
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    if (args.empty()) {
        return nullptr;
    }
    double frameRate = napiHelper.GetDouble(args[NUM_0]);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj != nullptr) {
        auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
        if (lottieHandler != nullptr) {
            float maxFrameRate = lottieHandler->GetLottieFileTotalFrameRate();
            if (frameRate == 0 || (maxFrameRate < frameRate && frameRate < frameRateLimits) ||
                (maxFrameRate < frameRateLimits && frameRate >= frameRateLimits)) {
                frameRate = maxFrameRate;
            } else if (frameRate <= 0) {
                return nullptr;
            } else if (frameRate >= frameRateLimits && maxFrameRate >= frameRateLimits) {
                frameRate = frameRateLimits;
            }
            DRAWING_LOGD("LottieControllerBridge::SetFrameRate frameRate:%{public}f", frameRate);
            ArkUI_ExpectedFrameRateRange expectFrameRateRange = {.min = static_cast<uint32_t>(frameRate),
                                                                 .max = static_cast<uint32_t>(frameRate),
                                                                 .expected = static_cast<uint32_t>(frameRate)};
            lottieHandler->Pause();
            lottieHandler->SetFrameRate(frameRate);
            lottieHandler->animator_->SetFrameRateRange(expectFrameRateRange);
            lottieHandler->animator_->UpdateFrameRateRange();
            lottieHandler->Play();
        }
    }
    return nullptr;
}

// Set 和 Get 方法不应该给LottieControllerBridge设值或取值，而是从对应的C++实现类里面取
napi_value LottieControllerBridge::SetId(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    auto lottieId = napiHelper.GetString(args[NUM_0]);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj != nullptr) {
        obj->lottieId_ = lottieId;
        DRAWING_LOGD("SetId %{public}s", lottieId.c_str());
    }
    return nullptr;
}

napi_value LottieControllerBridge::GetId(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj != nullptr) {
        return napiHelper.CreateString(obj->lottieId_);
    }
    return nullptr;
}

napi_value LottieControllerBridge::GetLoaded(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        return napiHelper.CreateBoolean(lottieHandler->GetIsLoaded());
    }

    return nullptr;
}

napi_value LottieControllerBridge::GetFirstFrame(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        return napiHelper.CreateDouble(lottieHandler->GetFirstFrame());
    }
    return nullptr;
}

napi_value LottieControllerBridge::SetCurrentFrame(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    auto currentFrame = napiHelper.GetDouble(args[NUM_0]);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler == nullptr) {
        lottieHandler->SetCurrentFrame(currentFrame);
        DRAWING_LOGD("SetCurrentFrame %{public}f", currentFrame);
    }

    return nullptr;
}

napi_value LottieControllerBridge::GetCurrentFrame(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        // 目前不支持子帧插值，只考虑返回整数值
        return napiHelper.CreateDouble(lottieHandler->GetCurrentFrame());
    }
    return nullptr;
}

napi_value LottieControllerBridge::SetCurrentRawFrame(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    auto currentRawFrame = napiHelper.GetInteger(args[NUM_0]);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        lottieHandler->SetCurrentFrame(currentRawFrame);
        DRAWING_LOGD("SetCurrentRawFrame %{public}d", currentRawFrame);
    }
    return nullptr;
}

napi_value LottieControllerBridge::GetCurrentRawFrame(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        return napiHelper.CreateDouble(lottieHandler->GetCurrentRawFrame());
    }
    return nullptr;
}

napi_value LottieControllerBridge::GetTotalFrames(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        return napiHelper.CreateDouble(lottieHandler->GetTotalFrames());
    }
    return nullptr;
}

napi_value LottieControllerBridge::GetDataFrameRate(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        return napiHelper.CreateDouble(lottieHandler->GetFrameRate());
    }
    return nullptr;
}

napi_value LottieControllerBridge::GetFrameMult(napi_env env, napi_callback_info info)
{
    constexpr float persent = 1000.0;
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        return napiHelper.CreateDouble(lottieHandler->GetFrameRate() / persent);
    }
    return nullptr;
}

napi_value LottieControllerBridge::GetPlayCount(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        return napiHelper.CreateInteger(lottieHandler->GetPlayCount());
    }
    return nullptr;
}

napi_value LottieControllerBridge::GetIsPaused(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        return napiHelper.CreateBoolean(lottieHandler->GetIsPaused());
    }
    return nullptr;
}

napi_value LottieControllerBridge::GetAutoPlay(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        return napiHelper.CreateBoolean(lottieHandler->GetLottieConfig()->GetAutoplay());
    }
    return nullptr;
}

napi_value LottieControllerBridge::GetLoop(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        auto loopValue = lottieHandler->GetLottieConfig()->GetLoop();
        if (std::holds_alternative<bool>(loopValue)) {
            return napiHelper.CreateBoolean(std::get<bool>(loopValue));
        } else if (std::holds_alternative<int>(loopValue)) {
            return napiHelper.CreateInteger(std::get<bool>(loopValue));
        }
    }
    return nullptr;
}

napi_value LottieControllerBridge::SetLoop(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto args = napiHelper.GetCallbackArgs(info);
    if (args.empty()) {
        return nullptr;
    }
    std::variant<bool, int> loop;
    if (napiHelper.CheckIsType(args[NUM_0], napi_boolean)) {
        loop = napiHelper.GetBoolean(args[NUM_0]);
    } else if (napiHelper.CheckIsType(args[NUM_0], napi_number)) {
        loop = static_cast<int>(napiHelper.GetDouble(args[NUM_0]));
    } else {
        DRAWING_LOGE("LottieControllerBridge::SetLoop Input Error");
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        lottieHandler->GetLottieConfig()->SetLoop(loop);
    }
    return nullptr;
}

napi_value LottieControllerBridge::GetTimeCompleted(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        return napiHelper.CreateDouble(lottieHandler->GetTimeCompleted());
    }
    return nullptr;
}

napi_value LottieControllerBridge::GetSegmentPos(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        return napiHelper.CreateInteger(static_cast<int32_t>(lottieHandler->GetSegmentPos()));
    }
    return nullptr;
}

napi_value LottieControllerBridge::GetIsSubframeEnabled(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        return napiHelper.CreateBoolean(lottieHandler->GetIsSubframeEnabled());
    }
    return nullptr;
}

napi_value LottieControllerBridge::GetDuration(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    bool isFrame = false;
    if (args.size() > 0) {
        isFrame = napiHelper.GetBoolean(args[NUM_0]);
    }
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        double duration = isFrame ? lottieHandler->GetTotalFrames()
                                  : lottieHandler->GetTotalFrames() / lottieHandler->GetLottieFileTotalFrameRate();
        return napiHelper.CreateDouble(duration);
    }
    return nullptr;
}

napi_value LottieControllerBridge::GetPlaySpeed(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        return napiHelper.CreateDouble(lottieHandler->GetPlaySpeed());
    }
    return nullptr;
}

napi_value LottieControllerBridge::GetPlayDirection(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler != nullptr) {
        return napiHelper.CreateInteger(static_cast<int32_t>(lottieHandler->GetDirection()));
    }
    return nullptr;
}

napi_value LottieControllerBridge::GetSegments(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto obj = napiHelper.UnwrapInstanceFromCallBack<LottieControllerBridge>(info);
    if (obj == nullptr) {
        return nullptr;
    }
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(obj->lottieId_);
    if (lottieHandler == nullptr) {
        return nullptr;
    }
    auto segments = lottieHandler->GetSegments();
    std::vector<napi_value> array;
    for (auto segment : segments) {
        auto element = napiHelper.CreateObject();
        napiHelper.addObjectProperty(element, napiHelper.CreateString("initFrame"),
                                     napiHelper.CreateDouble(segment.initFrame));
        napiHelper.addObjectProperty(element, napiHelper.CreateString("endFrame"),
                                     napiHelper.CreateDouble(segment.endFrame));
        array.push_back(element);
    }
    return napiHelper.CreateArray(array);
}

napi_value LottieControllerBridge::Construct(napi_env env, napi_callback_info info)
{
    const int32_t argSize = 2;
    size_t argc = argSize;
    napi_value argv[argSize] = {nullptr};
    napi_value jsThis;
    napi_get_cb_info(env, info, &argc, argv, &jsThis, nullptr);

    LottieControllerBridge *obj = new LottieControllerBridge();
    auto status = napi_wrap(
        env, jsThis, obj,
        [](napi_env env, void *finalize_data, void *finalize_hint) {
            delete reinterpret_cast<LottieControllerBridge *>(finalize_data);
        },
        nullptr, nullptr);
    if (status != napi_ok) {
        DRAWING_LOGE("LottieControllerBridge::Construct lottieId(%{public}s): "
                     "LottieControllerBridge wrap fail!",
                     obj->lottieId_.c_str());
        // wrap失败时需手动释放
        delete obj;
    }
    return jsThis;
}

static const napi_property_descriptor descClass[] = {
    {"play", nullptr, LottieControllerBridge::Play, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"stop", nullptr, LottieControllerBridge::Stop, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"togglePause", nullptr, LottieControllerBridge::TogglePause, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"destroy", nullptr, LottieControllerBridge::Destroy, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"pause", nullptr, LottieControllerBridge::Pause, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"goToAndStop", nullptr, LottieControllerBridge::GoToAndStop, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"goToAndPlay", nullptr, LottieControllerBridge::GoToAndPlay, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"setSegment", nullptr, LottieControllerBridge::SetSegment, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"resetSegments", nullptr, LottieControllerBridge::ResetSegments, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"setSpeed", nullptr, LottieControllerBridge::SetSpeed, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"setDirection", nullptr, LottieControllerBridge::SetDirection, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"playSegments", nullptr, LottieControllerBridge::PlaySegments, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"setSubframe", nullptr, LottieControllerBridge::SetSubframe, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"getDuration", nullptr, LottieControllerBridge::GetDuration, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"triggerEvent", nullptr, LottieControllerBridge::TriggerEvent, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"addEventListener", nullptr, LottieControllerBridge::AddEventListener, nullptr, nullptr, nullptr, napi_default,
     nullptr},
    {"removeEventListener", nullptr, LottieControllerBridge::RemoveEventListener, nullptr, nullptr, nullptr,
     napi_default, nullptr},
    {"changeColor", nullptr, LottieControllerBridge::ChangeColor, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"reload", nullptr, LottieControllerBridge::ReLoad, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"setContentMode", nullptr, LottieControllerBridge::SetContentMode, nullptr, nullptr, nullptr, napi_default,
     nullptr},
    {"setFrameRate", nullptr, LottieControllerBridge::SetFrameRate, nullptr, nullptr, nullptr, napi_default, nullptr},
    {"lottieId", nullptr, nullptr, LottieControllerBridge::GetId, LottieControllerBridge::SetId, nullptr, napi_default,
     nullptr},
    {"isLoaded", nullptr, nullptr, LottieControllerBridge::GetLoaded, nullptr, nullptr, napi_default, nullptr},
    {"currentFrame", nullptr, nullptr, LottieControllerBridge::GetCurrentFrame, nullptr, nullptr, napi_default,
     nullptr},
    {"currentRawFrame", nullptr, nullptr, LottieControllerBridge::GetCurrentRawFrame, nullptr, nullptr, napi_default,
     nullptr},
    {"firstFrame", nullptr, nullptr, LottieControllerBridge::GetFirstFrame, nullptr, nullptr, napi_default, nullptr},
    {"totalFrames", nullptr, nullptr, LottieControllerBridge::GetTotalFrames, nullptr, nullptr, napi_default, nullptr},
    {"frameRate", nullptr, nullptr, LottieControllerBridge::GetDataFrameRate, nullptr, nullptr, napi_default, nullptr},
    {"frameMult", nullptr, nullptr, LottieControllerBridge::GetFrameMult, nullptr, nullptr, napi_default, nullptr},
    {"playCount", nullptr, nullptr, LottieControllerBridge::GetPlayCount, nullptr, nullptr, napi_default, nullptr},
    {"playSpeed", nullptr, nullptr, LottieControllerBridge::GetPlaySpeed, nullptr, nullptr, napi_default, nullptr},
    {"playDirection", nullptr, nullptr, LottieControllerBridge::GetPlayDirection, nullptr, nullptr, napi_default,
     nullptr},
    {"isPaused", nullptr, nullptr, LottieControllerBridge::GetIsPaused, nullptr, nullptr, napi_default, nullptr},
    {"autoplay", nullptr, nullptr, LottieControllerBridge::GetAutoPlay, nullptr, nullptr, napi_default, nullptr},
    {"loop", nullptr, nullptr, LottieControllerBridge::GetLoop, LottieControllerBridge::SetLoop, nullptr, napi_default,
     nullptr},
    {"timeCompleted", nullptr, nullptr, LottieControllerBridge::GetTimeCompleted, nullptr, nullptr, napi_default,
     nullptr},
    {"segmentPos", nullptr, nullptr, LottieControllerBridge::GetSegmentPos, nullptr, nullptr, napi_default, nullptr},
    {"isSubframeEnabled", nullptr, nullptr, LottieControllerBridge::GetIsSubframeEnabled, nullptr, nullptr,
     napi_default, nullptr},
    {"segments", nullptr, nullptr, LottieControllerBridge::GetSegments, nullptr, nullptr, napi_default, nullptr},
};

napi_value LottieControllerBridge::Exports(napi_env env, napi_value &exports)
{
    napi_status status;
    napi_value cons;
    napi_ref *ref = new napi_ref;
    status = napi_define_class(env, "LottieController", NAPI_AUTO_LENGTH, LottieControllerBridge::Construct, nullptr,
                               sizeof(descClass) / sizeof(descClass[NUM_0]), descClass, &cons);
    status = napi_create_reference(env, cons, 1, ref);

    status = napi_set_named_property(env, exports, "LottieController", cons);

    return exports;
}

void LottieControllerBridge::SetLottieId(std::string lottieId)
{
    this->lottieId_ = lottieId;
}

} // namespace LottieTurbo